/**
 * 视域分析的插件
 */
import * as Cesium from 'cesium';
import viewShedPrimitive from "./viewShedPrimitive";
import mouseHelper from "../../MouseHelper";
import glsl from './index'
let  handler:any=null;
let viewer: any = {};
class ViewArea{
  viewPoint:any;
  entities:any;
  shadMapEntities:any;
  primitiveEntities:any;
  vishedfrustum:any;
  shadowMap:any;
  name:any;
  constructor() {
    //视点
    this.viewPoint=null;
    //两个视点的数组
    this.entities=[];
    this.shadMapEntities=[];
    this.primitiveEntities=[];
    this.vishedfrustum=[];
    this.shadowMap=null;
    this.name="";
  }

  /**
   * 开始-视域分析
   * @param viewer
   * @param height  观察点高度
   */
  startAnalysis(v:any,height:number,tipOffset:any){
    viewer=v;
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    let _this=this;
    //左键点击创建视点
    handler.setInputAction(function(evt: any) {
      let car3 = innerFun.getCar3Position(viewer,evt.position);
      _this.viewPoint=new Cesium.Cartesian3(car3.x, car3.y, car3.z+height);
      let entity=  _this.creaPointEntity(car3, "#008000");
      _this.entities.push(entity)
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);


    handler.setInputAction(function(evt: any) {
      if(mouseHelper.helperTips){
        mouseHelper.updateHelper(evt.endPosition, '左击设置起点,右击设置终点',tipOffset);
      }else{
        mouseHelper.createHelper(evt.endPosition, '左击设置起点,右击设置终点',tipOffset);
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    //鼠标右键点击事件--创建终点
    handler.setInputAction(function(evt:any)  {
      let car3 = innerFun.getCar3Position(viewer,evt.position);
      if(_this.viewPoint){
        let entity=  _this.creaPointEntity(car3, "#FF0000");
        _this.entities.push(entity);
        _this.createViewShedMap(_this.viewPoint,car3)
      }
      _this.endDrawing();
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)

  }

  /**
   * 创建视域分析
   * @param startPosition
   * @param endPosition
   */
  createViewShedMap (startPosition:any, endPosition:any) {
    let _this=this;
    let scene = viewer.scene
    //计算起点到终点的距离
    let viewDistance = Cesium.Cartesian3.distance(startPosition, endPosition)
    //贴近球面，水平方向，范围 90度
    const hr = Cesium.Math.toRadians(90)
    //垂直方向上，范围60度
    const vr = Cesium.Math.toRadians(60)
    const aspectRatio = (viewDistance * Math.tan(hr / 2) * 2) / (viewDistance * Math.tan(vr / 2) * 2);
    let spotLightCamera:any = new Cesium.Camera(scene)
    spotLightCamera.position = startPosition
    spotLightCamera.direction = this.calculateDirection(startPosition, endPosition);
    spotLightCamera.up = Cesium.Cartesian3.clone(viewer.camera.up);
    // 相机视锥体的参数
    spotLightCamera.frustum.aspectRatio = aspectRatio
    spotLightCamera.frustum.fov = hr;
    spotLightCamera.frustum.near = viewDistance * 0.01;
    spotLightCamera.frustum.far = viewDistance;
    spotLightCamera.setView({
      destination: startPosition,
      orientation: {
        heading: Cesium.Math.toRadians(this.getHeading(startPosition, endPosition)),
        pitch: Cesium.Math.toRadians(this.getPitch(startPosition, endPosition)),
        roll: 0
      }
    })
    //绘制视锥体
    this.drawFrustum(startPosition, endPosition, spotLightCamera.frustum);
    //绘制视网
    let entitySketch=  this.drawSketch(startPosition, endPosition,viewDistance)
    this.entities.push(entitySketch);
    //创建阴影贴图
    let viewShedOptions:any = {
      context: scene.context,
      lightCamera: spotLightCamera,
      enabled: true,
      isPointLight: true,
      pointLightRadius: viewDistance,
      cascadesEnabled: false,
      size: 1024,
      softShadows: true,
      normalOffset: false,
      fromLightSource: false
      // viewShed: true,
    };
    // @ts-ignore
    let viewShed = new Cesium.ShadowMap(viewShedOptions);
    viewShed.enabled = true;
    viewShed.size = 1024
    viewer.scene.shadowMap = viewShed
    //cesium1.115 这个位置有点区别
    scene.primitives.add(new viewShedPrimitive(viewShed));
    // this.shadMapEntities.push(scene.primitives.add(new viewShedPrimitive(viewShed)));
    this.createPostStage(spotLightCamera, viewShed, viewDistance)
  }
  calculateDirection (startPosition:any, endPosition:any) {
    return Cesium.Cartesian3.normalize(
      Cesium.Cartesian3.subtract(endPosition, startPosition, new Cesium.Cartesian3()),
      new Cesium.Cartesian3()
    );
  }
  //后处理的方法
  createPostStage (spotLightCamera:any, shadowMap:any, viewDistance:any) {
    const fs = glsl
    const postStage = new Cesium.PostProcessStage({
      fragmentShader: fs,
      uniforms: {
        shadowMap_textureCube: () => {
          shadowMap.update(Reflect.get(viewer.scene, "_frameState"));
          return Reflect.get(shadowMap, "_shadowMapTexture");
        },
        shadowMap_matrix: () => {
          shadowMap.update(Reflect.get(viewer.scene, "_frameState"));
          return Reflect.get(shadowMap, "_shadowMapMatrix");
        },
        shadowMap_lightPositionEC: () => {
          shadowMap.update(Reflect.get(viewer.scene, "_frameState"));
          return Reflect.get(shadowMap, "_lightPositionEC");
        },
        shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: () => {
          shadowMap.update(Reflect.get(viewer.scene, "_frameState"));
          const bias = shadowMap._pointBias;
          return Cesium.Cartesian4.fromElements(
            bias.normalOffsetScale,
            shadowMap._distance,
            shadowMap.maximumDistance,
            0.0,
            new Cesium.Cartesian4()
          );
        },
        shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: () => {
          shadowMap.update(Reflect.get(viewer.scene, "_frameState"));
          const bias = shadowMap._pointBias;
          const scratchTexelStepSize = new Cesium.Cartesian2();
          const texelStepSize = scratchTexelStepSize;
          texelStepSize.x = 1.0 / shadowMap._textureSize.x;
          texelStepSize.y = 1.0 / shadowMap._textureSize.y;

          return Cesium.Cartesian4.fromElements(
            texelStepSize.x,
            texelStepSize.y,
            bias.depthBias,
            bias.normalShadingSmooth,
            new Cesium.Cartesian4()
          );
        },
        camera_projection_matrix: spotLightCamera.frustum.projectionMatrix,
        camera_view_matrix: spotLightCamera.viewMatrix,
        helsing_viewDistance: () => {
          return viewDistance;
        },
        helsing_visibleAreaColor: Cesium.Color.GREEN,
        helsing_invisibleAreaColor: Cesium.Color.RED,
      }
    });
    viewer.scene.postProcessStages.add(postStage);
  }

  endDrawing() {
    if(handler){
      handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
      handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
      handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }
    mouseHelper.removeHelper();
  }
  //绘制视椎体
  drawFrustum (startPosition:any, endPosition:any, frustum:any) {
    let orientation = this.calculateorigntation(startPosition, endPosition);
    let primitive = new Cesium.Primitive({
      geometryInstances: new Cesium.GeometryInstance({
        geometry: new Cesium.FrustumOutlineGeometry({
          frustum: frustum,
          origin: startPosition,
          orientation: orientation
        }),
        attributes: {
          color: Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(1.0, 1.0, 0.0, 1.0))
        }
      }),
      appearance: new Cesium.PerInstanceColorAppearance({
        flat: true
      })
    });
    this.vishedfrustum = primitive;
    viewer.scene.primitives.add(primitive);
    this.primitiveEntities.push(primitive);
  }
  //创建视网
  drawSketch (startPosition:any, endPosition:any,viewDistance:any) {
  let entity=   viewer.entities.add({
      name: 'sketch',
      position: startPosition,
      orientation: Cesium.Transforms.headingPitchRollQuaternion(
        startPosition,
        Cesium.HeadingPitchRoll.fromDegrees(this.getHeading(startPosition, endPosition) - 90, this.getPitch(startPosition, endPosition), 0.0)
      ),
      ellipsoid: {
        radii: new Cesium.Cartesian3(
          viewDistance,
          viewDistance,
          viewDistance
        ),
        // innerRadii: new Cesium.Cartesian3(2.0, 2.0, 2.0),
        minimumClock: Cesium.Math.toRadians(-45),
        maximumClock: Cesium.Math.toRadians(45),
        minimumCone: Cesium.Math.toRadians(67.75),
        maximumCone: Cesium.Math.toRadians(112.25),
        fill: false,
        outline: true,
        subdivisions: 256,
        stackPartitions: 64,
        slicePartitions: 64,
        outlineColor: Cesium.Color.YELLOWGREEN
      }
    });
  return entity;
  }
  clearAnalysis () {
    this.endDrawing();
    // 移除一个后处理的方式
    let prs=viewer.scene.postProcessStages;
    if(prs.length>0){
      for(let i=prs.length-1;i>=0;i--){
        viewer.scene.postProcessStages.remove(prs.get(i));
      }
    }
    //雷达弧形网
    if(this.entities.length>0){
      this.entities.forEach((item:any)=>{
        viewer.entities.remove(item);
      })
    }
    //stop shadowMap
    viewer.scene.shadowMap.enabled = false;
    //清除视锥-最外漏斗
    if(this.primitiveEntities.length>0){
      this.primitiveEntities.forEach((item:any) => {
        viewer.scene.primitives.remove(item);
      });
    }

    // let primitives = viewer.scene.primitives;
    // let length = primitives.length;
    // for (let i = length-1; i >=0; i--) {
    //   let item = primitives.get(i);
    //     if (item.name === 'viewShedPRIMITIVE') {
    //       viewer.scene.primitives.remove(item);
    //     }
    // }

  }




  /**
   * 计算两点之间的水平的角度
   * @param fromPosition
   * @param toPosition
   */
  getHeading (fromPosition:any, toPosition:any) {
    let finalPosition = new Cesium.Cartesian3();
    let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
    Cesium.Matrix4.inverse(matrix4, matrix4);
    Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
    Cesium.Cartesian3.normalize(finalPosition, finalPosition);
    return Cesium.Math.toDegrees(Math.atan2(finalPosition.x, finalPosition.y));
  }

  /**
   * 计算两点之间的垂直方向的夹角
   * @param fromPosition
   * @param toPosition
   */
  getPitch (fromPosition:any, toPosition:any) {
    let finalPosition = new Cesium.Cartesian3();
    let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
    Cesium.Matrix4.inverse(matrix4, matrix4);
    Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
    Cesium.Cartesian3.normalize(finalPosition, finalPosition);
    return Cesium.Math.toDegrees(Math.asin(finalPosition.z));
  }



  creaPointEntity(car3:any,color:string){
    let entity=viewer.entities.add({
      position: car3,
      point: {
        pixelSize: 10,
        color: Cesium.Color.fromCssColorString(color)
      }
    })
    return entity;
  }


  calculateorigntation (p1:any, p2:any) {
    let co1 = this.cartesian2lonlat(p1);
    let co2 = this.cartesian2lonlat(p2);

    let width = this.catchDistancefromCartographic2D(
      co1.longitude,
      co1.latitude,
      co2.longitude,
      co1.latitude
    );
    let length = this.catchDistancefromCartographic2D(
      co1.longitude,
      co1.latitude,
      co1.longitude,
      co2.latitude
    );
    let height = co1.height - co2.height;
    let distance = this.catchDistancefromCartographic2D(
      co1.longitude,
      co1.latitude,
      co2.longitude,
      co2.latitude
    );

    let angle = Math.atan(width / length);
    let tilt = Math.atan(height / distance);

    if (co1.latitude > co2.latitude) {
      angle = Math.PI - angle;
    }
    if (co1.longitude > co2.longitude) {
      angle = -angle;
    }

    let heading = angle;
    let pitch = 0;
    let roll = -Math.PI / 2 - tilt;
    let hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
    return Cesium.Transforms.headingPitchRollQuaternion(p1, hpr);
  }


  //笛卡尔转经纬高
  cartesian2lonlat (cartesian:any) {
    let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
    return {
      longitude: Cesium.Math.toDegrees(cartographic.longitude), //经度
      latitude: Cesium.Math.toDegrees(cartographic.latitude), //纬度
      height: cartographic.height //高度
    };
  }
  /**
   * 计算及经纬度之间的距离
   */
  catchDistancefromCartographic2D (longitude1:any, latitude1:any, longitude2:any, latitude2:any) {
    let p1 = Cesium.Cartesian3.fromDegrees(longitude1, latitude1, 0);
    let p2 = Cesium.Cartesian3.fromDegrees(longitude2, latitude2, 0);
    return Cesium.Cartesian3.distance(p1, p2);
  }

}
export default new  ViewArea();

/**
 * 内部的公共的方法
 */
const innerFun = {
  /**
   * 鼠标的事件获取当前的点击位置的笛卡尔坐标
   * @param position 鼠标点击之后的evt.position
   */
  getCar3Position: function(viewer:any,position: any) {
    let car3: any = null;
    let pick = viewer.scene.pick(position);
    if (viewer.scene.globe.depthTestAgainstTerrain) {
      if (Cesium.defined(pick)) {//获取到了pick
        car3 = viewer.scene.pickPosition(position);
      } else {
        let ray = viewer.camera.getPickRay(position);//获取一条射线
        car3 = viewer.scene.globe.pick(ray, viewer.scene);
      }
    } else {
      car3 = viewer.scene.camera.pickEllipsoid(position, viewer.scene.globe.ellipsoid);
    }
    return car3;
  }
};
